#include "MultiLeave_Test.h"

#include "server/multiProtocol/multiLeave.h"
#include "server/multiProtocol/multiRing.h"
#include "server/net/NetworkControl.h"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string.h>
/*********************************************************/
std::string MultiLeaveTest::name()
{
	return("MultiLeave");
}
/*********************************************************/
bool MultiLeaveTest::runUnitTests()
{
	bool result = true;

	if ( !multiLeaveSerializeTest() ) result = false;
	if ( !multiLeaveSingleNodeRingTest() ) result = false;
	if ( !multiLeaveDataUpdateTest() ) result = false;

	return( result ) ;
}
/*********************************************************/
bool MultiLeaveTest::multiLeaveSingleNodeRingTest()
{
	std::cout << "Running: multiLeaveSingleNodeRingTest()";
	std::cout.flush();

	try {
		// Start the network controller
		NetworkControl& nc = NetworkControl::Instance();
		nc.start();

		// Get a ring
		MultiRing	mRing;
		mRing.setHash("0");
		mRing.setPredecessorAddr("0.0.0.0:6000");
		mRing.setAddr("0.0.0.0:6000");
		mRing.setSuccessorAddr("0.0.0.0:6000");
		mRing.execute();

		// Leave the ring
		MultiLeave	mLeave;
		mLeave.setAddr("0.0.0.0:6000");
		mLeave.execute();

		// Check if networkcontrol returns the correct ring information
		std::ostringstream	 ostr;
		ostr << "Ring in multicastgroup: " << "239.0.0.1:5001" << std::endl;
		ostr << "--------------------------------------" << std::endl;
		ostr << std::setw(25) << std::left << "Addr" << std::setw(25) << "Pred" << std::setw(25) << "Succ" << std::setw(40) << "Hash" << std::endl;

		// Check if the node was correctly removed from the ring
		if ( strcmp(ostr.str().c_str(), nc.getRingInformation().c_str()) == 0 ) {
			nc.stop();
			std::cout << "... success!" << std::endl;
			return true;
		} else {
			std::cout << "... failed! Invalid ring information stored in NetworkControl" << std::endl;
			std::cout << "NetworkControl returned: " << std::endl;
			std::cout << nc.getRingInformation() << std::endl;
			std::cout << "Expected: " << std::endl;
			std::cout << ostr.str() << std::endl;
			nc.stop();
			return false;
		}
		
		nc.stop();
	} catch(...) {
		std::cout << "... failed! Caught unknown exception... expected none" << std::endl;
		return false;
	}

	std::cout << "... failed!" << std::endl;
	return false;
}
/*********************************************************/
bool MultiLeaveTest::multiLeaveDataUpdateTest() 
{
	std::cout << "Running: multiLeaveDataUpdateTest()";
	std::cout.flush();

	try {
		// Start the network controller
		NetworkControl& nc = NetworkControl::Instance();
		nc.start();

		// Execute some multiring message to build a ring
		MultiRing	mRing;
		mRing.setHash("9999988877632534");
		mRing.setPredecessorAddr("0.0.0.0:6001");
		mRing.setSuccessorAddr("0.0.0.0:6003");
		mRing.setAddr("0.0.0.0:6000");
		mRing.execute();

		mRing.setHash("EF1234");
		mRing.setAddr("0.0.0.0:6001");
		mRing.setPredecessorAddr("0.0.0.0:6002");
		mRing.setSuccessorAddr("0.0.0.0:6000");
		mRing.execute();

		mRing.setHash("FFFFFFFFFFFFFFFFFFF");
		mRing.setAddr("0.0.0.0:6002");
		mRing.setPredecessorAddr("0.0.0.0:6003");
		mRing.setSuccessorAddr("0.0.0.0:6001");
		mRing.execute();

		mRing.setHash("FFFFFFFFFFFFFFF1");
		mRing.setAddr("0.0.0.0:6003");
		mRing.setPredecessorAddr("0.0.0.0:6000");
		mRing.setSuccessorAddr("0.0.0.0:6002");
		mRing.execute();

		// Create and execute a leave for one of the nodes
		MultiLeave	mLeave;
		mLeave.setAddr("0.0.0.0:6003");
		mLeave.execute();

		// Build the expected output string for getRingInformation
		std::ostringstream	 ostr;
		ostr << "Ring in multicastgroup: " << "239.0.0.1:5001" << std::endl;
		ostr << "--------------------------------------" << std::endl;
		ostr << std::setw(25) << std::left << "Addr" << std::setw(25) << "Pred" << std::setw(25) << "Succ" << std::setw(40) << "Hash" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6001" << std::setw(25) << "0.0.0.0:6002" << std::setw(25) << "0.0.0.0:6000" << std::setw(40) << "EF1234" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6000" << std::setw(25) << "0.0.0.0:6001" << std::setw(25) << "0.0.0.0:6002" << std::setw(40) << "9999988877632534" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6002" << std::setw(25) << "0.0.0.0:6000" << std::setw(25) << "0.0.0.0:6001" << std::setw(40) << "FFFFFFFFFFFFFFFFFFF" << std::endl;

		// Check if the node was correctly removed from the ring
		if ( strcmp(ostr.str().c_str(), nc.getRingInformation().c_str()) == 0 ) {
			nc.stop();
			std::cout << "... success!" << std::endl;
			return true;
		} else {
			std::cout << "... failed! Invalid ring information stored in NetworkControl" << std::endl;
			std::cout << "NetworkControl returned: " << std::endl;
			std::cout << nc.getRingInformation() << std::endl;
			std::cout << "Expected: " << std::endl;
			std::cout << ostr.str() << std::endl;
			nc.stop();
			return false;
		}
		
		nc.stop();
	} catch(...) {
		std::cout << "... failed! Caught unknown exception... expected none" << std::endl;
		return false;
	}

	std::cout << "... success!" << std::endl;
	return true;
}
/*********************************************************/
bool MultiLeaveTest::multiLeaveSerializeTest() 
{
	std::cout << "Running: multiLeaveSerializeTest()";
	std::cout.flush();
	try {
		MultiLeave	mLeave;
		mLeave.setAddr("127.0.0.1:5000");

		std::string xml = mLeave.serialize();
		
		if ( strcmp(xml.c_str(), "<MGS type='LEAVE'><addr>127.0.0.1:5000</addr></MGS>") == 0 ) {
			std::cout << "... success!" << std::endl;
			return true;
		} else {
			std::cout << "... failed! Invalid serialization of a multi leave packet!" << std::endl;
			return false;
		}
	} catch(...) {
		std::cout << "... failed! Caught unexpected exception!" << std::endl;
		return false;
	}
	
	std::cout << "... failed! impossible test outcome!" << std::endl;
	return false;
}
